﻿using System;
using System.Collections.Generic;
using System.Web.Services;
using System.IO;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections;

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
// [System.Web.Script.Services.ScriptService]
public class Service : System.Web.Services.WebService
{
    private const int MAX_OLD_MESSAGES = 50;
    private const string PROFILE_DATA_EXT = ".wpd";
    private const string PROFILE_OPTIONS_EXT = ".wpo";
    private const string PROFILE_IMAGE_EXT = ".img";
    private const string LOG_DIR = "c:\\sites\\Single41\\waynehart\\webroot\\WGM_Data\\V2\\SystemData\\Logs";
    private const string USER_MESSAGES_DIR = "Messages";
    private const string USER_PROFILE_DIR = "c:\\sites\\Single41\\waynehart\\webroot\\WGM_Data\\V2\\Users\\profiles";
    private const string USER_OPTIONS_DIR = "c:\\sites\\Single41\\waynehart\\webroot\\WGM_Data\\V2\\Users\\info";
    private const string ONLINE_USERS_DIR = "OnlineUsers";
    private const string SYSTEM_DIR = "c:\\sites\\Single41\\waynehart\\webroot\\WGM_Data\\V2\\SystemData";
    private const string TEMP_DIR = "c:\\sites\\Single41\\waynehart\\webroot\\WGM_Data\\V2\\Temp";
    private const string NETWORKS_DIR = "c:\\sites\\Single41\\waynehart\\webroot\\WGM_Data\\V2\\Networks";
    private const string NETWORK_INFO_FILE_NAME = "info.wgn";
    private const string MEMBER_DIRECTORY = "Members";
    public Service () 
    {

        //Uncomment the following line if using designed components 
        //InitializeComponent(); 
    }
    private void SetUpDirectoryStructure()
    {
        if (!Directory.Exists(LOG_DIR))
            Directory.CreateDirectory(LOG_DIR);
        if (!Directory.Exists(USER_PROFILE_DIR))
            Directory.CreateDirectory(USER_PROFILE_DIR);
        if (!Directory.Exists(USER_OPTIONS_DIR))
            Directory.CreateDirectory(USER_OPTIONS_DIR);
        if (!Directory.Exists(TEMP_DIR))
            Directory.CreateDirectory(TEMP_DIR);
        if (!Directory.Exists(NETWORKS_DIR))
            Directory.CreateDirectory(NETWORKS_DIR);
    }
    public UserProfile GetUserData(string userName)
    {
        string fileName = USER_PROFILE_DIR + "\\" + userName + PROFILE_DATA_EXT;
        if (File.Exists(fileName))
        {
            XmlSerializer xs = new XmlSerializer(typeof(UserProfile));
            FileStream fs = File.OpenRead(fileName);
            UserProfile user = xs.Deserialize(fs) as UserProfile;
            fs.Close();
            return user;

        }
        return null;
    }
    private void RemoveOnlineUser(string userName, string macAddress, string network)
    {
        LogMessage("calling remove on " + userName + "@" + macAddress + " in "+network);
        if (File.Exists(NETWORKS_DIR+"\\"+network+"\\"+ ONLINE_USERS_DIR + "\\" + userName+"\\"+macAddress))
            File.Delete(NETWORKS_DIR + "\\" + network + "\\" + ONLINE_USERS_DIR + "\\" + userName + "\\" + macAddress);
    }
    private void SetupNetworkStructure(string path)
    {
        if (!Directory.Exists(path + "\\"+USER_MESSAGES_DIR))
            Directory.CreateDirectory(path + "\\"+USER_MESSAGES_DIR);
        if (!Directory.Exists(path + "\\"+MEMBER_DIRECTORY))
            Directory.CreateDirectory(path + "\\"+MEMBER_DIRECTORY);
        if (!Directory.Exists(path + "\\"+ONLINE_USERS_DIR))
            Directory.CreateDirectory(path + "\\"+ONLINE_USERS_DIR);
    }
    [WebMethod]
    public bool CreateNetwork(string userName, string password, string networkName, string networkPassword,string macAddress, string networkDescription, out string message)
    {
        bool retVal = false;
        if (userName == null || userName.Trim() == "")
        {
            message = "invalid user name";
            return false;
        }
        message = "Trying to create network: " + networkName+"\r\n";
        userName = userName.ToLower();
        try
        {
            SetUpDirectoryStructure();
            message = "Authenticating...\r\n";
            if (AuthenticateUser(userName, password, out message))
            {
                message = "Authentication successful...\r\n";
                CheckOnlineUsers();
                string networkPath = NETWORKS_DIR + "\\" + networkName;
                if (Directory.Exists(networkPath))
                {
                    message += "Network " + networkName + " already exists\r\n";
                    return false;
                }
                else
                {
                    Directory.CreateDirectory(networkPath);
                    SetupNetworkStructure(networkPath);
                    SetupUserDirectoryStructure(userName, networkName, macAddress);
                    string networkInfoPath = networkPath + "\\"+NETWORK_INFO_FILE_NAME;
                    WGMNetwork newNetwork = new WGMNetwork(networkName,WEncryption.WCryptor.Encrypt( networkPassword), userName, networkDescription);
                    SaveObject(newNetwork, networkInfoPath);
                    message += "Network " + networkName + " successfully created\r\n";
                    LogMessage(message + " for " + userName);
                    AddUserToNetwork(userName, networkName);
                    return true;
                }
            }
            else
            {
                LogMessage("Failed user login: " + userName);
                return false;
            }
        }
        catch (Exception ex)
        {
            LogError(ex, "Created network  " + networkName + " for "+userName);
            message += ex.Message + "\r\n\r\n";
        }
        message +="Unable to create network";
        return retVal;
    }
    [WebMethod]
    public List<WGMNetwork> GetNetworks(string userName, string password, out string message)
    {
        List<WGMNetwork> retVal = new List<WGMNetwork>();
        userName = userName.ToLower();
        if(!AuthenticateUser(userName, password,out message))
        {
            return null;
        }
        message = "retrieving networks...\r\n";
        DirectoryInfo root = new DirectoryInfo(NETWORKS_DIR);
        DirectoryInfo[] subDirs = root.GetDirectories();
        foreach (DirectoryInfo subDir in subDirs)
        {
            message += "Found network: " + subDir.Name+"\r\n";
            DirectoryInfo memberDir = new DirectoryInfo(subDir.FullName + "\\"+MEMBER_DIRECTORY);
            bool isMember = false;
            FileInfo[] memberFiles = memberDir.GetFiles();
            int memberCount = memberFiles.Length;
            foreach (FileInfo fi in memberFiles)
            {
                if (fi.Name.ToLower() == userName)
                    isMember = true;
            }
            string infoPath = subDir.FullName + "\\"+NETWORK_INFO_FILE_NAME;
            if (File.Exists(infoPath))
            {
                WGMNetwork network = LoadObject(infoPath, typeof(WGMNetwork)) as WGMNetwork;
                if (network != null)
                {
                    network.NetworkMembersCount = memberCount;
                    network.IsRequesterAMember = isMember;
                    network.NetworkPassword = "";
                    retVal.Add(network);
                }
            }
        }
        message += "success";
        return retVal;
    }
    [WebMethod]
    public List<string> JoinNetwork(string userName, string password, string networkName, string networkPassword,string macAddress, out string message)
    {        
        try
        {
            userName = userName.ToLower();
            if (!AuthenticateUser(userName, password, out message))
            {
                return null;
            }
            string networkPath = NETWORKS_DIR + "\\" + networkName;
            string networkInfoPath = networkPath + "\\" + NETWORK_INFO_FILE_NAME;
            if (!File.Exists(networkInfoPath))
            {
                message = "no such network " + networkName;
                return null;
            }
            WGMNetwork network = LoadObject(networkInfoPath, typeof(WGMNetwork)) as WGMNetwork;
            if (network != null)
            {
                LogMessage("adding " + userName + " to network " + networkName + " from " + macAddress);
                string userFile = NETWORKS_DIR + "\\" + networkName + "\\" + MEMBER_DIRECTORY + "\\" + userName;
                LogMessage("Checking for " + userFile);
                if (File.Exists(userFile))
                {
                    LogMessage("User " + userName + " exists in "+networkName);
                    SetupUserDirectoryStructure(userName, networkName, macAddress);
                    WGMMessage userOnline = new WGMMessage(userName, "", userName, MessageTypes.USER_LOGON, DateTime.Now);
                    DepositMessageToAllOnline(userOnline, networkName);
                    AddUserToOnlineList(userName, macAddress,networkName);
                    return GetNetworkMembers(networkPath);
                }
                if (WEncryption.WCryptor.Decrypt(network.NetworkPassword) != networkPassword)
                {
                    message = "invalid password for network " + networkName;
                    return null;
                }
                else
                {

                    AddUserToNetwork(userName, networkName);
                    WGMMessage newUser = new WGMMessage(userName, "", userName, MessageTypes.USER_JOINED_NETWORK, DateTime.Now);
                    DepositMessageToAllOnline(newUser, networkName);
                    SetupUserDirectoryStructure(userName, networkName, macAddress);
                    AddUserToOnlineList(userName,macAddress,networkName);
                    return GetNetworkMembers(networkPath);
                }
            }
        }
        catch (Exception ex)
        {
            message = ex.StackTrace;
            return null;
        }
        message = "failed";
        return null;
    }

    private void AddUserToNetwork(string userName, string networkName)
    {
        
        string userFile = NETWORKS_DIR + "\\" + networkName + "\\" + MEMBER_DIRECTORY + "\\" + userName;
        WGMMessage userCreated = new WGMMessage(userName, "", userName, MessageTypes.USER_JOINED_NETWORK, DateTime.Now);
        DepositMessageToAllOnline(userCreated, networkName);
        FileStream fs = File.Create(userFile);
        fs.Close();
    }

    private static void SetupUserDirectoryStructure(string userName, string networkName, string macAddress)
    {
        string messageDir = NETWORKS_DIR + "\\" + networkName + "\\" + USER_MESSAGES_DIR + "\\" + userName;
        if (!Directory.Exists(messageDir))
            Directory.CreateDirectory(messageDir);
        string messageDirMac = messageDir + "\\" + macAddress;
        if (!Directory.Exists(messageDirMac))
            Directory.CreateDirectory(messageDirMac);
    }



    private List<string> GetNetworkMembers(string networkPath)
    {
        List<string> retVal = new List<string>();
        string membersPath = networkPath + "\\"+MEMBER_DIRECTORY;
        FileInfo[] memberFiles = new  DirectoryInfo(membersPath).GetFiles();
        foreach (FileInfo memberFile in memberFiles)
        {
            retVal.Add(memberFile.Name);
        }
        return retVal;
    }
    [WebMethod]
    public bool CreateUser(string userName, string password, string secureQ, string secureA, out string message)
    {
        //message = "Server is locked... please try again later";
        //return false;
        userName = userName.ToLower();
        if (userName == "main" || userName=="main_conversation" || userName == "system")
        {
            message = "The username " + userName + " is reserved for the system use";
            return false;
        }
        try
        {
            SetUpDirectoryStructure();

            string[] allUsers = Directory.GetFiles(USER_PROFILE_DIR);

            foreach (string user in allUsers)
            {
                if ((user).ToLower().Trim().EndsWith((userName + PROFILE_DATA_EXT).ToLower().Trim()))
                {
                    message = "User " + userName + " already exists...";
                    return false;
                }
            }

            string filepath = USER_PROFILE_DIR + "\\" + userName + PROFILE_DATA_EXT;
            UserProfile newProfile = new UserProfile(userName, WEncryption.WCryptor.Encrypt(password), secureQ, WEncryption.WCryptor.Encrypt(secureA));
            SaveObject(newProfile, filepath);
            message = "User " + userName + " successfully created!";
            LogMessage(message);

            string userOptionsPath = USER_OPTIONS_DIR + "\\" + userName + PROFILE_OPTIONS_EXT;
            if (!File.Exists(userOptionsPath))
            {
                UserOptions newUserOpt = new UserOptions(userName, userName, "New User", "Arial", 12, 0, "");
                SaveObject(newUserOpt, userOptionsPath);
                LogMessage("options created " + userOptionsPath);
            }
            return true;
        }
        catch (Exception ex)
        {
            LogError(ex, "Create User "+userName);
            message = "system error";
            return false;
        }
    }


    [WebMethod]
    public bool LogIn(string userName, string password, string macAddress, out string message)
    {
        if (userName == null || userName.Trim() == "")
        {
            message = "invalid user name";
            return false;
        }
        userName = userName.ToLower();
        try
        {
            SetUpDirectoryStructure();
            if (AuthenticateUser(userName, password, out message))
            {
                LogMessage("User logging in to system: " + userName + "@" + macAddress);
                return true;
            }
            else
            {
                LogMessage("Failed user login: " + userName + " - " + macAddress);
                return false;
            }
        }
        catch (Exception ex)
        {
            LogError(ex, "Login "+userName);

        }
        message = "system error";
        return false;
    }

    [WebMethod]
    public bool LogOff(string userName, string password, string macAddress, string network)
    {
        userName = userName.ToLower();
        string statusMessage = "";

        if (!AuthenticateUser(userName, password, out statusMessage))
            return false;            

        try
        {
            RemoveOnlineUser(userName,macAddress, network);
            WGMMessage userLeaving = new WGMMessage(userName, "", userName, MessageTypes.USER_LEAVING, DateTime.Now);
            DepositMessageToAllOnline(userLeaving,network);
            LogMessage("User left: " + userName + "@"+macAddress);
            statusMessage = "success";
            return true;
        }
        catch (Exception ex)
        {
            LogMessage("There was a failure to tell online users about " + userName + " leaving\r\n");
            LogError(ex, "Logoff "+userName);

            statusMessage = "system error:\r\n" + ex.ToString();
            return false;
        }
    }
    [WebMethod]
    public List<string> GetOnlineUserNames(string network)
    {
        try
        {
            string onlineUsersPath = NETWORKS_DIR + "\\" + network + "\\" + ONLINE_USERS_DIR;
            List<string> retVal = new List<string>();
            if (Directory.Exists(onlineUsersPath))
            {
                DirectoryInfo di = new DirectoryInfo(onlineUsersPath);
                DirectoryInfo[] subDirs = di.GetDirectories();
                foreach (DirectoryInfo subDir in subDirs)
                {
                    if(subDir.GetFiles().Length > 0)
                        retVal.Add(subDir.Name.ToLower());
                }

            }
            return retVal;
        }
        catch (Exception ex)
        {
            LogError(ex, "GetOnlineUserNames");
            return null;
        }
    }
    [WebMethod]
    public void SendMessage(WGMMessage message, string userName, string password,string network, out string statusMessage)
    {
        if (message.SenderLogon == null || message.SenderLogon == "")
        {
            statusMessage = "invalid user name";
            return;
        }
        if (!AuthenticateUser(userName, password, out statusMessage))
        {
            return;
        }
        if (CheckUserOnline(message.SenderLogon, network).Count == 0)
        {
            statusMessage = "offline users cannot send messages";
            return;
        }
        message.TimeStamp = GetServerTime();
        try
        {
            switch (message.MessageType)
            {
                case MessageTypes.GLOBAL_MESSAGE:
                case MessageTypes.USER_CHANGED_INFO:
                case MessageTypes.USER_LEAVING:
                case MessageTypes.USER_LOGON:
                    DepositMessageToAllOnline(message, network);
                    break;
                case MessageTypes.PRIVATE_MESSAGE:
                    DepositMessage(message.ReceiverLogon, message, network);
                    DepositMessage(message.SenderLogon, message, network);
                    break;
            }
        }
        catch (Exception ex)
        {
            LogError(ex, "Send message "+userName);
            statusMessage = "system error";

        }
    }
    private List<string> CheckUserOnline(string userName, string network)
    {
        List<string> retVal = new List<string>();
        if (userName == null || userName.Trim() == "")
        {
            return retVal;
        }
        userName = userName.ToLower();
        try
        {
            if (Directory.Exists(NETWORKS_DIR + "\\" + network + "\\" + ONLINE_USERS_DIR + "\\" + userName))
            {
                DirectoryInfo di = new DirectoryInfo(NETWORKS_DIR + "\\" + network + "\\" + ONLINE_USERS_DIR + "\\" + userName);
                FileInfo[] files = di.GetFiles();
                if (files.Length > 0)
                {                    
                    foreach (FileInfo fi in files)
                    {
                        retVal.Add(fi.Name);
                    }
                    return retVal;
                }
            }
            return retVal;
        }
        catch (Exception ex)
        {
            LogError(ex, "Check user online "+userName );
        }
        return retVal;
    }

    [WebMethod]
    public bool RetrieveMessages(string userName, string password,string network, string macAddress, out List<WGMMessage> messages, out string statusMessage)
    {
        if (userName == null || userName.Trim() == "")
        {
            statusMessage = "Invalid user name";
            messages = null;
            return false;
        }
        userName = userName.ToLower();
        statusMessage = "ok so far";
        if (!AuthenticateUser(userName, password, out statusMessage))
        {
            messages = null;
            return false;
        }
        try
        {
            if (CheckUserOnline(userName, network).Count == 0)
            {
                statusMessage = "You are currently offline";
                messages = null;
                return false;
            }
            AddUserToOnlineList(userName,macAddress, network);
            CheckOnlineUsers();
            string offlineMessagePath = NETWORKS_DIR + "\\" + network + "\\" + USER_MESSAGES_DIR + "\\" + userName;
            messages = new List<WGMMessage>();
            int max = 20;
            if (Directory.Exists(offlineMessagePath))
            {
                DirectoryInfo di = new DirectoryInfo(offlineMessagePath);
                FileInfo[] fileInfos = di.GetFiles();
                string[] messageFiles = Directory.GetFiles(offlineMessagePath);


                foreach (FileInfo messageFile in fileInfos)
                {
                    WGMMessage newMessage = LoadObject(messageFile.FullName, typeof(WGMMessage)) as WGMMessage;
                    if (newMessage != null)
                    {
                        messages.Add(newMessage);
                        messageFile.Delete();
                    }
                    

                    if (messages.Count >= max)
                    {
                        statusMessage = "success";
                        return true;
                    }
                }
            }

            string folderPath = NETWORKS_DIR+"\\"+network+"\\"+ USER_MESSAGES_DIR + "\\" + userName+"\\"+macAddress;

            if (Directory.Exists(folderPath) && messages.Count < max)
            {

                DirectoryInfo di = new DirectoryInfo(folderPath);
                FileInfo[] fileInfos = di.GetFiles();
                string[] messageFiles = Directory.GetFiles(folderPath);
                

                foreach (FileInfo messageFile in fileInfos)
                {
                    WGMMessage newMessage = LoadObject(messageFile.FullName, typeof(WGMMessage)) as WGMMessage;
                    if (newMessage != null)
                    {
                        messages.Add(newMessage);
                        messageFile.Delete();
                    }
                    

                    if (messages.Count >= max)
                        break;
                }
                statusMessage = "success";
                return true;
            }
            statusMessage = "no such path: " + folderPath;
            messages = null;
            return false;
        }
        catch (Exception ex)
        {
            LogError(ex, "retrieve messages "+userName+"@"+macAddress);
            statusMessage = ex.ToString();
            messages = null;
            return false;
        }
    }


    private void CheckOnlineUsers()
    {
        if (Directory.Exists(NETWORKS_DIR))
        {
            DirectoryInfo di = new DirectoryInfo(NETWORKS_DIR);
            DirectoryInfo[] networks = di.GetDirectories();
            foreach (DirectoryInfo network in networks)
            {
                DirectoryInfo onlineUsersDir = new DirectoryInfo(network.FullName + "\\" + ONLINE_USERS_DIR);
                DirectoryInfo[] onlineUsers = onlineUsersDir.GetDirectories();
                foreach (DirectoryInfo sub in onlineUsers)
                {
                    bool wasOnline = false;
                    FileInfo[] files = sub.GetFiles();
                    foreach (FileInfo fi in files)
                    {
                        DateTime creation = fi.LastWriteTime;
                        DateTime now = DateTime.Now;
                        TimeSpan elapsed = now - creation;
                        if (elapsed.TotalSeconds > 30)
                        {
                            RemoveOnlineUser(sub.Name, fi.Name, network.Name);
                            wasOnline = true;
                        }
                    }
                    if (di.GetFiles().Length == 0 && wasOnline)
                    {
                        WGMMessage userLeaving = new WGMMessage(sub.Name, "", sub.Name, MessageTypes.USER_LEAVING, DateTime.Now);
                        DepositMessageToAllOnline(userLeaving, network.Name);
                    }
                }
            }
        }  
    }

    private void DepositMessageToAllOnline(WGMMessage message, string networkName)
    {
        List<string> onlineUsers = GetOnlineUserNames(networkName);
        if (onlineUsers != null)
        {
            foreach (string onlineUser in onlineUsers)
            {
                DepositMessage(onlineUser, message, networkName);
            }
        }
    }
    private void DepositMessage(string userName, WGMMessage message, string network)
    {
        userName = userName.ToLower();

        List<string> macAddresses = CheckUserOnline(userName, network);
        if (macAddresses.Count == 0)
        {
            string messagePath = NETWORKS_DIR + "\\" + network + "\\" + USER_MESSAGES_DIR + "\\" + userName + "\\message-" + DateTime.Now.ToString("MddyyyHHmmssffff");
            SaveObject(message, messagePath);
        }
        else
        {
            foreach (string macAddress in macAddresses)
            {
                if (!Directory.Exists(NETWORKS_DIR + "\\" + network + "\\" + USER_MESSAGES_DIR + "\\" + userName + "\\" + macAddress))
                    Directory.CreateDirectory(NETWORKS_DIR + "\\" + network + "\\" + USER_MESSAGES_DIR + "\\" + userName + "\\" + macAddress);
                string messagePath = NETWORKS_DIR + "\\" + network + "\\" + USER_MESSAGES_DIR + "\\" + userName + "\\" + macAddress + "\\message-" + DateTime.Now.ToString("MddyyyHHmmssffff");
                SaveObject(message, messagePath);
            }
        }


    }


    [WebMethod]
    public List<string> GetAllUserNames(string userName, string password, out string statusMessage)
    {
        try
        {
            if (!AuthenticateUser(userName, password, out statusMessage))
            {
                return null;
            }
            List<string> retVal = new List<string>();
            if (Directory.Exists(USER_PROFILE_DIR))
            {
                DirectoryInfo di = new DirectoryInfo(USER_PROFILE_DIR);
                FileInfo[] allFiles = di.GetFiles();
                foreach (FileInfo fi in allFiles)
                {
                    retVal.Add(fi.Name.ToLower());
                }

            }
            return retVal;
        }
        catch (Exception ex)
        {
            LogError(ex, "Get all user names for "+userName);
            statusMessage = "system error";
            return null;
        }
    }
    [WebMethod]
    public UserOptions GetUserOptions(string userName, string requesterUserName, string password, out string statusMessage)
    {
        userName = userName.ToLower();
        try
        {
            if (!AuthenticateUser(requesterUserName, password, out statusMessage))
            {
                return null;
            }
            string fileName = USER_OPTIONS_DIR + "\\" + userName + PROFILE_OPTIONS_EXT;
            if (File.Exists(fileName))
            {
                return (LoadObject(fileName, typeof(UserOptions)) as UserOptions);
            }
            return null;
        }
        catch (Exception ex)
        {
            LogError(ex, "Get user options for "+userName + " by "+requesterUserName);
            statusMessage = "system error";
            return null;
        }
    }
    [WebMethod]
    public List<UserOptions> GetAllUserOptions(string userName, string password,string networkName, out string statusMessage)
    {
        try
        {
            if (!AuthenticateUser(userName, password, out statusMessage))
            {
                return null;
            }
            string networkPath = NETWORKS_DIR + "\\" + networkName;
            string networkMembersPath = networkPath + "\\" + MEMBER_DIRECTORY;
            if (!Directory.Exists(networkPath))
            {
                statusMessage = "No such network: " + networkName;
                return null;
            }
            if (!Directory.Exists(networkMembersPath))
            {
                statusMessage = "Error getting members of "+networkName;
                return null;
            }
            List<UserOptions> retVal = new List<UserOptions>();
            FileInfo[] members = new DirectoryInfo(networkMembersPath).GetFiles();
            foreach (FileInfo fi in members)
            {
                string infoPath = USER_OPTIONS_DIR + "\\" + fi.Name + PROFILE_OPTIONS_EXT;
                if (File.Exists(infoPath))
                {
                    UserOptions options = LoadObject(infoPath, typeof(UserOptions)) as UserOptions;
                    retVal.Add(options);
                }
            } 
            return retVal;
        }
        catch (Exception ex)
        {
            LogError(ex, "get all user options for "+userName);
            statusMessage = "system error";
            return null;
        }
    }
    [WebMethod]
    public DateTime GetServerTime()
    {
        return DateTime.Now;
    }

    private void LogMessage(string message)
    {
        if (!Directory.Exists(LOG_DIR))
            Directory.CreateDirectory(LOG_DIR);
        string fileName = LOG_DIR + "\\messagelog.log";


        StreamWriter sw = File.AppendText(fileName);
        sw.WriteLine(DateTime.Now.ToString() + " - " + message+"\r\n");
        sw.Close();
    }
    private void LogError(Exception ex, string caller)
    {

        try
        {
            if (!Directory.Exists(LOG_DIR))
                Directory.CreateDirectory(LOG_DIR);

            string fileName = LOG_DIR + "\\errlog.log";

            StreamWriter sw = File.AppendText(fileName);
            sw.WriteLine("\r\n"+DateTime.Now.ToString() + " - caller: " + caller + "\r\n" + ex.ToString());
            sw.Close();
        }
        catch { }

    }
    [WebMethod]
    public byte[] GetUserImage(string userName, string requesterUserName, string password, out string statusMessage)
    {
        userName = userName.ToLower();
        try
        {
            if (!AuthenticateUser(requesterUserName, password, out statusMessage))
            {
                return null;
            }
            string imagePath = USER_OPTIONS_DIR + "\\" + userName + PROFILE_IMAGE_EXT;
            if (File.Exists(imagePath))
            {

                Stream stream = new FileStream(imagePath, System.IO.FileMode.Open, FileAccess.Read, FileShare.Delete);
                IFormatter formatter = new BinaryFormatter();
                byte[] retVal = (formatter.Deserialize(stream)) as byte[];
                stream.Close();
                return retVal;
            }
            return null;
        }
        catch (Exception ex)
        {
            LogError(ex, "Get user image for "+userName+ " by "+requesterUserName);
            statusMessage = "system error";
            return null;
        }

    }
    [WebMethod]
    public void SetUserImage(string userName, string password,string currentNetwork, out string statusMessage, byte[] image)
    {
        if (userName == null || userName.Trim() == "")
        {
            statusMessage = "invalid user name";
            return;
        }

        userName = userName.ToLower();
        try
        {
            if (!AuthenticateUser(userName, password, out statusMessage))
            {
                return;
            }
            string imagePath = USER_OPTIONS_DIR + "\\" + userName + PROFILE_IMAGE_EXT;
            if (image == null)
            {
                File.Delete(imagePath);
                return;
            }
            string networkPath = NETWORKS_DIR + "\\" + currentNetwork;
            if (!Directory.Exists(networkPath))
            {
                statusMessage = "No such network" + networkPath;
            }
            Stream stream = new FileStream(imagePath, System.IO.FileMode.Create, FileAccess.Write, FileShare.Delete);
            IFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, image);
            stream.Close();
            LogMessage("Setting new image for user: " + userName);
            WGMMessage newMessage = new WGMMessage(userName, "", null, MessageTypes.USER_CHANGED_IMAGE, DateTime.Now);
            DepositMessageToAllOnline(newMessage, currentNetwork);
        }
        catch (Exception ex)
        {
            LogError(ex, "Set user image by "+userName);
            statusMessage = "system error";
        }
    }
    [WebMethod]
    public void SetUserOptions(string username, string password,string currentNetwork, out string statusMessage, UserOptions options)
    {
        if (username == null || username.Trim() == "")
        {
            statusMessage = "invalid user name";
            return;
        }
        if (options.UserName == null || options.UserName.Trim() == "")
        {
            statusMessage = "invalid options";
            return;
        }
        string networkPath = NETWORKS_DIR + "\\" + currentNetwork;
        if (!Directory.Exists(networkPath))
        {
            statusMessage = "No such network" + networkPath;
        }

        username = username.ToLower();
        try
        {
            if (!AuthenticateUser(username, password, out statusMessage))
            {
                return;
            }
            string filePath = USER_OPTIONS_DIR + "\\" + username + PROFILE_OPTIONS_EXT;
            SaveObject(options, filePath);
            LogMessage("Setting new options for user: " + username + "\r\n" + options.DisplayName + "\r\n" + options.Email + "\r\n" + options.FontColour + "\r\n" + options.FontSize + "\r\n" + options.FontStyle + "\r\n" + options.FontStyleInt + "\r\n" + options.UserFontFace + "\r\n" + options.UserStatus);
            WGMMessage newMessage = new WGMMessage(username, "", null, MessageTypes.USER_CHANGED_INFO, DateTime.Now);
            DepositMessageToAllOnline(newMessage,currentNetwork);
        }
        catch (Exception ex)
        {
            LogError(ex, "set user options by "+username);
            statusMessage = "system error";

        }
    }
    private void AddUserToOnlineList(string userName, string macAddress, string network)
    {
        userName = userName.ToLower();
        try
        {
            if (!Directory.Exists(NETWORKS_DIR + "\\" + network + "\\" + ONLINE_USERS_DIR + "\\" + userName))
                Directory.CreateDirectory(NETWORKS_DIR + "\\" + network + "\\" + ONLINE_USERS_DIR + "\\" + userName);
            string onlineFilePath = NETWORKS_DIR + "\\" + network + "\\" + ONLINE_USERS_DIR + "\\" + userName + "\\" + macAddress;
            FileStream fs = File.Create(onlineFilePath);
            fs.Close();
        }
        catch (Exception ex)
        {
            LogError(ex, "add to online " +userName+"@"+macAddress);

        }

    }
    private void SaveObject(object obj, string path)
    {
        string tempLoc = TEMP_DIR + "\\" + obj.GetHashCode() + DateTime.Now.ToString("MddyyyHHmmssffff") + ".tmp";
        XmlSerializer ser = new XmlSerializer(obj.GetType());
        FileStream fs = File.Create(tempLoc);
        ser.Serialize(fs, obj);
        fs.Close();
        if(File.Exists(path))
        {
            LogError(new Exception("WTF? File already exists: "+path),"Save object "+tempLoc +" to "+path + " \r\nAttemtping to overwrite..." );
            File.Copy(tempLoc, path,true);
            return;
        }
        File.Move( tempLoc, path);
    }
    private object LoadObject(string path, Type type)
    {
        XmlSerializer xs = new XmlSerializer(type);
        FileStream fs = File.Open(path,FileMode.Open,FileAccess.Read,FileShare.ReadWrite);

        object retVal = xs.Deserialize(fs);
        fs.Close();
        return retVal;
    }


    private bool AuthenticateUser(string username, string password, out string message)
    {
        username = username.ToLower();
        try
        {
            string fileName = USER_PROFILE_DIR + "\\" + username + PROFILE_DATA_EXT;
            if (File.Exists(fileName))
            {
                XmlSerializer xs = new XmlSerializer(typeof(UserProfile));
                FileStream fs = File.OpenRead(fileName);
                UserProfile user = xs.Deserialize(fs) as UserProfile;
                fs.Close();
                if (WEncryption.WCryptor.Decrypt(user.Password) == password)
                {
                    message = "Success";
                    return true;
                }
                else
                {
                    message = "Incorrect password";
                    return false;
                }
            }
            else
            {
                message = "No such user: " + username + "\r\nOr the server is down for repairs";
                return false;
            }
        }
        catch (Exception ex)
        {
            LogError(ex, "authenticate "+username);
            message = "system error";
            return false;
        }
    }
    private UserOptions GetUserOptionsLocal(string userName)
    {
        userName = userName.ToLower();
        try
        {
            string fileName = USER_OPTIONS_DIR + "\\" + userName + PROFILE_OPTIONS_EXT;
            if (File.Exists(fileName))
            {
                return (LoadObject(fileName, typeof(UserOptions)) as UserOptions);
            }
            return null;
        }
        catch (Exception ex)
        {
            LogError(ex, "Get user options for " + userName);
            return null;
        }
    }


}
